﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;

namespace ClearingRestartingAutomaton.Base
{
    /// <summary>
    /// Immutable structure representing the collection of instructions.
    /// </summary>
    [Serializable]
    public struct InstructionCollection : IComparable<InstructionCollection>, IEquatable<InstructionCollection>
    {
        #region Constructors

        /// <summary>
        /// Constructor of this structure.
        /// </summary>
        /// <param name="instructions">The enumeration of instructions.</param>
        public InstructionCollection(IEnumerable<Instruction> instructions)
        {
            this.instructions = new List<Instruction>(instructions);
            this.instructions.Sort();
        }

        #endregion

        #region IComparable Interface

        /// <summary>
        /// Compares this instance with a specified collection of instructions and indicates
        /// whether this instance precedes, follows, or appears in the same position
        /// in the sort order as the specified collection of instructions.
        /// </summary>
        /// <param name="instructionCollection">Collection of instructions.</param>
        /// <returns>
        /// A 32-bit signed integer that indicates whether this instance precedes, follows,
        /// or appears in the same position in the sort order as the value parameter.
        /// Value Condition Less than zero: This instance precedes the value parameter. 
        /// Zero: This instance has the same position in the sort order as the value parameter. 
        /// Greater than zero: This instance follows the value parameter.
        /// </returns>
        public int CompareTo(InstructionCollection instructionCollection)
        {
            int cmpLengths = this.instructions.Count.CompareTo(instructionCollection.instructions.Count);
            if (cmpLengths != 0) { return cmpLengths; }
            for (int i = 0; i < this.instructions.Count; ++i)
            {
                int cmpInstructions = this.instructions[i].CompareTo(instructionCollection.instructions[i]);
                if (cmpInstructions != 0) { return cmpInstructions; }
            }
            return 0;
        }

        #endregion

        #region IEquatable Interface

        /// <summary>
        /// Determines whether this instance of InstructionCollection and a specified object,
        /// which must also be an InstructionCollection object, have the same value.
        /// </summary>
        /// <param name="obj">An System.Object.</param>
        /// <returns>
        /// True if obj is an InstructionCollection and its value is the same as this instance;
        /// Otherwise, false.
        /// </returns>
        public override bool Equals(object obj)
        {
            if (obj is InstructionCollection)
                return this.Equals((InstructionCollection)obj);
            else
                return false;
        }

        /// <summary>
        /// Determines whether this instance and another specified InstructionCollection object have the same value.
        /// </summary>
        /// <param name="instructionCollection">InstructionCollection instance.</param>
        /// <returns>
        /// True if the value of the value parameter is the same as this instance; 
        /// Otherwise, false.
        /// </returns>
        public bool Equals(InstructionCollection instructionCollection)
        {
            bool equalLengths = this.instructions.Count.Equals(instructionCollection.instructions.Count);
            if (!equalLengths) { return false; }
            for (int i = 0; i < this.instructions.Count; ++i)
            {
                bool equalInstructions = this.instructions[i].Equals(instructionCollection.instructions[i]);
                if (!equalInstructions) { return false; }
            }
            return true;
        }

        /// <summary>
        /// Returns the hash code for this instruction.
        /// </summary>
        /// <returns>A 32-bit signed integer hash code.</returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the enumeration of (all) instructions.
        /// </summary>
        public ReadOnlyCollection<Instruction> Instructions
        {
            get { return this.instructions.AsReadOnly(); }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Indicates whether this collection of instructions is a subset of the other
        /// specified collection of instructions.
        /// </summary>
        /// <param name="instructionCollection">The other collection of instructions.</param>
        /// <returns>
        /// Returns true, if this collection of instructions is a subset of the other
        /// specified collection of instructions. Otherwise, returns false.
        /// </returns>
        public bool IsSubset(InstructionCollection instructionCollection)
        {
            int index = 0;
            foreach (Instruction instruction in this.instructions)
            {
                bool checkFound = false;
                while (index < instructionCollection.instructions.Count)
                {
                    if (instruction.Equals(instructionCollection.instructions[index]))
                    {
                        checkFound = true;
                        ++index;
                        break;
                    }
                    ++index;
                }
                if (!checkFound)
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Returns a System.String that represents this collection of instructions.
        /// </summary>
        /// <returns>A System.String that represents this collection of instructions.</returns>
        public override string ToString()
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (Instruction instruction in this.instructions)
            {
                stringBuilder.AppendLine(instruction.ToString());
            }
            return stringBuilder.ToString();
        }

        #endregion

        #region Private Data Members

        private List<Instruction> instructions;

        #endregion
    }
}
